package com.mrx.commons.utils.jarloader;

import com.mrx.commons.trace.TraceUtils;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.ExecutorService;

/**
 * Jar 加载器 的默认实现
 *
 * @author Mr.X
 * @since 2023-12-08 11:10
 **/
public class JarLoader implements ILoader, InvocationHandler {

    private final ClassLoader classLoader;

    private static final ExecutorService pool = TraceUtils.newFixedTracerPool(10);

    private static final Logger logger = LoggerFactory.getLogger(JarLoader.class);

    private JarLoader(URL... classpath) {
        classLoader = new URLClassLoader(classpath);
    }

    public static ILoader newLoader(URL... classpath) {
        ClassLoader parentLoader = JarLoader.class.getClassLoader();
        return (ILoader) Proxy.newProxyInstance(parentLoader, new Class[]{ILoader.class}, new JarLoader(classpath));
    }

    @SneakyThrows
    public Class<?> loadClass(String name) {
        logger.info("loadClass: {}", name);
        return classLoader.loadClass(name);
    }

    @SneakyThrows
    public void loadAndInvokeMain(String name) {
        Class<?> clazz = loadClass(name);
        Method main = clazz.getMethod("main", String[].class);
        main.invoke(null, (Object) new String[]{});
    }

    public static void main(String[] args) throws Exception {
        ILoader jarLoader = JarLoader.newLoader(new File("./files/MXShell-1.0-all.jar").toURI().toURL());
        System.out.println(jarLoader);
        jarLoader.loadAndInvokeMain("com.mrx.mxshell.Main");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("toString")) {
            return JarLoader.class + "$ENHANCED_BY_JAR_LOADER";
        }
        return pool.submit(() -> method.invoke(this, args)).get();
    }
}
